#pragma once
#ifndef ARRAYLIST__H__
#define ARRAYLIST__H__
#ifndef _AUTOMOBILE_H
#include "..\engine\memorymanager.h"
#endif
#include "def.h"
#include <string.h>

#ifdef _DEBUG
#include "..\engine\error.h"
#endif

/**************************************
*ArrayList
*Dynamic list object for object storage
 **************************************/
template <class T>
class EE_EXPORT ArrayList
{
private:
	static const unsigned int STEP_SIZE = 10; 

	unsigned int	m_Count; 
	unsigned int	m_Capacity; 
	T				*m_pData; 

public:
	ArrayList(void); 

	virtual ~ArrayList(void); 
	T& operator[] (const unsigned int idx); 
	T* Data(void); 
	void Add(T* ptr, unsigned int count); 
	void Add(T ptr); 
	void Empty(void); 
	void ClearAllocatedSpace(void); 
	void Remove(T ptr); 
	void Remove(unsigned int index); 
	void Flush();
	void Sort(bool (*pAGreaterThanB) (T pA, T pB));
	unsigned int Count(void) const; 
}; 

/**************************************
*
 **************************************/
template<class T>
ArrayList<T>::ArrayList(void) : m_Count(0), m_Capacity(STEP_SIZE)
{	
	m_pData = EE_NEW T [STEP_SIZE]; 
}

/**************************************
*
 **************************************/
template<class T>
ArrayList<T>::~ArrayList(void)
{	
	EE_SAFEDELETEARRAY(m_pData); 
}

/**************************************
 *Attempts to 'shave off' any allocated
 *memory that isn't required with respect
 *to it's current size
 **************************************/
template<class T>
void ArrayList<T>::Flush()
{
	if(m_Count != m_Capacity && m_Count > 0)
	{
		m_Capacity = m_Count; 
		T* pRealloc = EE_NEW T [ m_Count ]; 

		memcpy(pRealloc, m_pData, sizeof(T) * m_Count); 

		EE_SAFEDELETEARRAY(m_pData); 
		m_pData = pRealloc; 
	}
}

/**************************************
 *Allows elements to be sorted according
 *to a custom function
 **************************************/
template<class T>
void ArrayList<T>::Sort(bool (*pAGreaterThanB) (T pA, T pB))
{
	if(!pAGreaterThanB || m_Count == 0)
		return;

	bool sorted = false;

	while(!sorted)
	{
		sorted = true;
		for(unsigned int i = 0; i < m_Count - 1; i++)
		{
			if(pAGreaterThanB(m_pData[i], m_pData[i + 1]))
			{
				T pTemp = m_pData[i + 1];
				m_pData[i + 1] = m_pData[i];
				m_pData[i] = pTemp;
				sorted = false;
			}
		}
	}
}


/**************************************
 *Appends an object into the array list
 **************************************/
template<class T>
void ArrayList<T>::Add(T ptr)
{
	if(ISNULL(m_pData))
	{
		m_pData = EE_NEW T [STEP_SIZE];

		if(ISNULL(m_pData)) {
			EE_LOGERROR("Bad state"); 
			return; 
		}

		m_Capacity = STEP_SIZE;
		m_Count = 0;
	}

	if(m_Count >= m_Capacity)
	{
		m_Capacity += STEP_SIZE; 

		T* pRealloc = EE_NEW T [m_Capacity]; 
		
		memcpy(pRealloc, m_pData, sizeof(T) * m_Count); 

		EE_SAFEDELETEARRAY(m_pData); 
		m_pData = pRealloc; 
	}

	m_pData[m_Count] = ptr; 	
	m_Count ++; 
}

/**************************************
 *Appends objects to the array list
 **************************************/
template<class T>
void ArrayList<T>::Add(T* ptr, unsigned int count)
{
#ifdef _DEBUG
	if(!ptr || count == 0)
	{
		EE_LOGERROR("Bad parameters"); 
		return; 
	}
#endif
	if(m_Count + count >= m_Capacity)
	{
		const unsigned int iAppendSize = STEP_SIZE * ((int)ceilf((float)count / (float)STEP_SIZE))
		m_Capacity += iAppendSize; 

		T* pRealloc = EE_NEW T [m_Capacity]; 
		
		memcpy(pRealloc, m_pData, sizeof(T) * m_Count); 

		EE_SAFEDELETEARRAY(m_pData); 
		m_pData = pRealloc; 
	}

	memcpy(&m_pData[m_Count], ptr, sizeof(T) * count)
	m_Count += count; 
}

/**************************************
 *Removes all objects from the list
 **************************************/
template<class T>
void ArrayList<T>::Empty(void)
{
	EE_SAFEDELETEARRAY(m_pData); 

	m_Capacity = 0; 
	m_Count = 0; 
}

/**************************************
 *Clears all allocated space within the 
 *list
 **************************************/
template<class T>
void ArrayList<T>::ClearAllocatedSpace(void)
{
	EE_ZEROMEMORY(m_pData, sizeof(T) * m_Capacity); 
	m_Count = 0; 
}

/**************************************
 *Removes an element from the list based
 *on an index
 **************************************/
template<class T>
void ArrayList<T>::Remove(unsigned int index)
{
	if(index > m_Count - 1 || m_Count == 0)
		return; 

	m_pData[index] = m_pData[m_Count - 1]; 

	m_Count--; 
}

/**************************************
 *Removes an element from the list based
 *on it's pointer
 **************************************/
template<class T>
void ArrayList<T>::Remove(T ptr)
{
	if(m_pData && m_Count > 0)
	{
		for(unsigned int i = 0;  i < m_Count;  i++)
		{
			if(m_pData[i] == ptr)
			{
				if(i != m_Count - 1)
					m_pData[i] = m_pData[m_Count - 1]; 

				m_pData[m_Count - 1] = NULL; 
				m_Count--; 
			}
		}
	}
}

/**************************************
 *Returns the number of elements in the 
 *list
 **************************************/
template<class T>
unsigned int ArrayList<T>::Count(void) const
{ 
	return m_Count;  
}

/**************************************
 *Returns a pointer to the beginning of
 *the lists data
 **************************************/
template<class T>
T* ArrayList<T>::Data(void) 
{
	Flush(); 
	return m_pData; 	
}

/**************************************
 *Indexing operator for data access
 **************************************/
template<class T>
T& ArrayList<T>::operator[] (const unsigned int idx)
{
#ifdef _DEBUG
	if(idx < m_Count && idx >= 0 && m_pData)
		return m_pData[idx]; 
	throw EE_EXCEPTION("Attempt to access invalid array list index")
#else
	return m_pData[idx]; 
#endif

}

#endif ARRAYLIST__H__